// let str = 'hello world';
//
// // str.substring(初始索引, 结束索引)
// // console.log(str.substring(6, 11));
//
// str.charAt(); // 取索引值对应的值
//
// str.charCodeAt(); //获取字符的
//
// str.indexOf();//从左往右依次查找字符的索引
//
// str.lastIndexOf();//从右往左依次查找字符的索引
//
// str.search();//正则查找字符的索引
//
// str.includes();//判断字符串里是否有括号里传入的字符
//
// str.concat();//添加字符串
//
// let str2 = 'xxxxx (yyyyy) xxxxx';
// // 我想得到 ( 和 ) 的索引位置？
// const left = str2.indexOf('(');
// const right = str2.lastIndexOf(')');
// // substring 并且将括号内的内容截取出来
// // console.log(str2.substring(left, right + 1));
//
//
// let str3 = 'x = (a + b) * (a - b) - 2ab';
// // 我想拿到两个括号内的内容，怎么写？
// const a = str3.indexOf('a');
// const a1 = str3.indexOf('b');
// const b = str3.lastIndexOf('a', 25 - 1);
// const b1 = str3.lastIndexOf('b', 26 - 1);
// console.log(1, str3.substring(a, a1 + 1));
// console.log(2, str3.substring(b, b1 + 1), b, b1);
//
// let str4 = '(a / b) - x = (a + b) * (a - b) / (b - c) * (d + a) * (e * b) / (f / a) * (b + g) * (i % b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) * (a - b) / (b - a) * (b + a) + 5';
// 拿到所有括号内的内容
// 循环

// 先干什么
// 后干什么
// 怎么干，最后能实现题目的要求
/**
 * 1. 先取到两个括号下标
 * 2. 然后循环调用后面括号的下标
 * 3. 用 substring 输出循环到的括号下标 + 1
 * 正确思路
 * 1. 先找到第一个左括号的索引，和第一个右括号的索引。
 * 2. 进入循环，截取出左右括号中间的字符串
 * 3. 然后更新左括号索引到下一个左括号，更新右括号索引到下一个右括号
 * 4. 直到索引值为-1时，结束循环
 */

// let leftIndex = 0;
// let rightIndex = 0;
//
// const arr = [];
//
// while (leftIndex > -1 && rightIndex > -1) {
//     leftIndex = str4.indexOf('(', leftIndex > 0 ? leftIndex + 1 : 0);
//     rightIndex = str4.indexOf(')', rightIndex > 0 ? rightIndex + 1 : 0);
//
//     if (leftIndex < 0 || rightIndex < 0) {
//         break;
//     }
//
//     arr.push(str4.substring(leftIndex + 1, rightIndex + 1 - 1));
//     if (leftIndex === 0) leftIndex += 1;
// }
//
// // 最简版的代码解释器: 解读文本、操作文本
//
// // 栈
// const stack = {
//     a: 1,
//     b: 2,
//     c: 3,
//     d: 4,
//     e: 5,
//     f: 6,
//     g: 7,
//     i: 8,
// };
//
// let tokens = ['+', '-', '*', '/', '%'];
//
// console.log(arr);
//
// for (let code of arr) {
//     code = code.replace(/\s+/g, '');
//     for (const token of tokens) {
//         if (code.includes(token)) {
//             // console.log(code.split(token));
//             const [a, b] = code.split(token); // ['a', 'b'];
//             // console.log(a, b);
//             const aValue = stack[a];
//             const bValue = stack[b];
//             // console.log(stack, stack[a], stack[b]);
//             const result = eval(`${aValue} ${token} ${bValue}`);
//             console.log(`${aValue} ${token} ${bValue} = ${result}`);
//         }
//     }
// }

// 作业1：https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Text_formatting 每一个方法点进去抄一遍，理解每个函数的意思。红色的跳过。
// const sentence = 'The quick brown fox jumps over the lazy dog.'
// const index = 4;
// console.log(`The character at index ${index} is ${sentence.charAt(index)}`)
//
// console.log(`character code ${sentence.charCodeAt(index)} is equal to ${sentence.charAt(index)}`)
//
// const icons = 'asd'
// console.log(icons.codePointAt(0));
//
// const paragraph = "I think Ruth's dog is cuter than your dog!";
// const searchTerm = 'dog';
// const index1 = paragraph.indexOf(searchTerm);
// console.log(`this ${searchTerm} is in ${index1}`);
//
// console.log(`Index of the last ${searchTerm} is ${paragraph.lastIndexOf(searchTerm)}`);
//
// const str1 = 'Saturday night plans';
// console.log(str1.startsWith('Sat'))
// console.log(str1.startsWith('day',5))
//
// const stri = 'Cats are the best!';
// console.log(stri.endsWith('best!'));
// console.log(stri.endsWith('best!',17));
// const strin = 'Is this a question?';
// console.log(strin.endsWith('question'));
//
// const sentence1 = 'The quick brown fox jumps over the lazy dog.';
// const word = 'fox';
// console.log(`the word "${word}"${sentence1.includes(word)?'is':'is not'} in the sentence1`)
//
// const str_ing = 'Hello';
// const str_ing1 = 'World';
// console.log(str_ing.concat('',str_ing1));
// console.log(str_ing1.concat(', ', str_ing));
//
// console.log(String.fromCharCode(189, 43, 190, 61));
//
// const str6 = 'The quick brown fox jumps over the lazy dog.';
// const words = str6.split(' ');
// console.log(words[3]);
// const chars = str.split('');
// console.log(chars[8]);
// const strCopy = str.split();
// console.log(strCopy);
//
// const str7 = 'The quick brown fox jumps over the lazy dog.';
// console.log(str7.slice(31));
// console.log(str7.slice(4, 19));
// console.log(str7.slice(-9, -5));
//
// const str8 = 'Mozilla';
// console.log(str8.substring(1, 3));
//
// const paragraph1 = "I think Ruth's dog is cuter than your dog!";
// console.log(paragraph1.replace("Ruth's", 'my'));
//
// const sentence2 = 'The quick brown fox jumps over the lazy dog.';
// console.log(sentence2.toLowerCase());// toLowerCase toLowerCase toLowerCase
// console.log(sentence2.toUpperCase());//
//
// const mood = 'Happy! ';
// console.log(`I feel ${mood.repeat(3)}`);
//
// const greeting = '   Hello world!   ';
// console.log(greeting.trim());

// 作业2：参考上面的代码完成下面字符串的解析（尝试）
// 假设：a = 1, b = 2, c = 3, d = 4, e = 5, f = 6


let arr = [
    // '(a - c)',
    // '(a + b * (a - c))',
    // '(b - f)',
    // '((b - f) * d)',
    // '(d + e)'
];


// let last = 0;
// let fast = 0;
// while (last > -1 && fast > -1) {
//     last = str5.indexOf('(', last > 0 ? last + 1 : 0);
//     fast = str5.indexOf(')', fast > 0 ? fast + 1 : 0);
//
//     arr.push(str5.substring(last + 1, fast + 1));
//
//     if (last === 0) last += 1;
// }


let calcResult = {};//存储已经计算过的子表达式及其结果
let operation = ['*', '/', '+', '-']//数组定义了算术操作符

// 通过给定字符串，和目标字符所在位置，向左或向右寻找 +-*/() 所在位置
/**
 * c + a - b * c + d / f
 * code 给定字符串
 * targetIndex 目标字符串所在位置
 * isToRight 是否时从左往右找
 */
function getVarIndex(code, targetIndex, isToRight = true) {
    const opIndexList = [];

    // 如果时从左往右的话
    if (isToRight) {
        // 循环运算符（解构）
        for (const op of [...operation, '(', ')']) {//循环operation 里的值加上（）并赋值给op
            const opIndex = code.indexOf(op, targetIndex + 1);//字符串从左往右遍历赋值给opIndex
            if (opIndex > -1) {//如果存在传给opIndexList
                opIndexList.push(opIndex);
            }
        }

        return Math.min(code.length, ...opIndexList);//返回一个最小值
    } else {
        for (const op of [...operation, '(', ')']) {//否的时候从右往左遍历
            opIndexList.push(code.lastIndexOf(op, targetIndex - 1));
        }

        return Math.max(0, ...opIndexList);
    }
}

function createVar(varValue) {//生成一个随机变量名
    const varSuffix = parseInt(Math.random().toString().substring(2)).toString(16);
    const varName = `var_${varSuffix}`;
    globalVars[varName] = varValue;
    return varName;
}

function calcStatArr(statArr) {
    const topLevelStat = {};//用于存储最终被识别为顶层表达式的计算结果
    for (let code of statArr) {//循环遍历数组中每一个表达式
        let tempCode = code;//复制code 到变量 tempCode 中

        for (const result in calcResult) {//遍历 calcResult 对象中的所有值
            const varName = createVar(calcResult[result])//生成一个新的变量名称，并用这个新变量名替换掉 tempCode 中对应的结果字符串
            tempCode = tempCode.replaceAll(result, varName);//把值赋值给自己
        }

        for (const token of operation) {
            if (code.includes(token)) {//判断code是否存在token
                let index = tempCode.indexOf(token);//定位操作符的位置，并进入循环继续查找后续出现的操作符
                while ((index = tempCode.indexOf(token)) > -1) {
                    const leftIndex = getVarIndex(tempCode, index, false);//调用 getVarIndex 函数
                    const rightIndex = getVarIndex(tempCode, index, true);
                    console.log(index, tempCode, leftIndex, rightIndex);
                    const var1 = tempCode.substring(tempCode[leftIndex].match(/[+\-*/()]/) ? leftIndex + 1 : leftIndex, index);
                    const var2 = tempCode.substring(index + 1, rightIndex);//提取左侧和右侧的操作数 var1 和 var2

                    // var1 或 var2 为负数或为空时
                    if (!var1 || !var2 || !var1.replace(/[+\-*/]/g, '') || !var2.replace(/[+\-*/]/g, '')) {
                        break;//检查提取出来的操作数是否有效，如果无效则跳出当前循环
                    }

                    // 拼接（算术运算表达式）CPU -> +-*/% >> << >>>
                    const calc = `${globalVars[var1] || var1} ${token} ${globalVars[var2] || var2}`;
                    // 执行计算，拿到结果
                    const result = eval(calc);

                    let stat = tempCode.substring(leftIndex, rightIndex + 1);//tempCode 的左右下标

                    if (stat[0].match(/[+\-*/]/) || (stat.startsWith('(') && !stat.endsWith(')'))) {
                        stat = stat.substring(1);//如果有右括号就执行
                    }

                    if (stat[stat.length - 1].match(/[+\-*/]/) || (!stat.startsWith('(') && stat.endsWith(')'))) {
                        stat = stat.substring(0, stat.length - 1);//如果有左括号就执行
                    }

                    calcResult[stat] = result;//将表达式保存到 calcResult 中

                    if (result < 0) {//更新tempCode将计算后的结果替换原来的表达式  如果是负数结果创建一个新的变量名来表示该结果
                        const varName = createVar(result);
                        tempCode = tempCode.replaceAll(stat, varName);
                    } else {
                        tempCode = tempCode.replaceAll(stat, result);
                    }
                }
            }
        }

        let isTop = true;//设立布尔标志 isTop 默认为 true
        for (let code2 of statArr) {//再次遍历 statArr 数组 检查是否有其他表达式包含了当前正在处理的表达式
            if (code !== code2 && code2.includes(code)) {
                isTop = false;
            }
        }

        if (isTop) {
            topLevelStat[code] = tempCode;
        }
    }

    return topLevelStat;
}

function getCalcArr(statStr) {
    const calcArr = []
    let tempArr = [];
    for (let index = 0; index < statStr.length; index++) {//遍历字符串 statStr 的每一个字符
        // console.log(str5[index]);
        if (statStr[index] === '(') {//遇到左括号( 添加到 tempArr 中
            tempArr.push('(')
        } else if (statStr[index] === ')') {//遇到右括号) 找到对应的左括号添加到 calcArr 中
            tempArr[tempArr.length - 1] += ')';
            // console.log(tempArr);
            const stat = tempArr.pop();
            calcArr.push(stat.replace(/\s+/g, ''));
            tempArr[tempArr.length - 1] += stat;
        } else {//其他字符追加到 tempArr 的最后一个元素中
            tempArr[tempArr.length - 1] += statStr[index];
        }
    }
    return calcArr;
}

/**
 * 分析文本，解析文本
 * 通过给定任意字符串表达式，计算表达式的值并返回
 * @param str
 * @returns {number|*}
 */
function calc(str) {
    const arr = getCalcArr(str);

    // 算下级表达式的值
    const result = calcStatArr(arr);
    let tempStr5 = str.replace(/\s+/g, '');
    for (const stat in result) {
        tempStr5 = tempStr5.replaceAll(stat, result[stat]);
    }

    // 算顶级表达式的值
    const result2 = calcStatArr([tempStr5]);

    const key = Object.keys(result2)[0];

    if (isNaN(+result2[key])) {
        return globalVars[result2[key]];
    }

    return +result2[key];
}

let globalVars = {
    a: 1,
    b: 2,
    c: 3,
    d: 4,
    e: 5,
    f: 6,
};

const result = calc('(a * a + b * d * c / f * (f - a)) + ((f - d) * d) * (d + e)');

console.log(result);

// 目标：彻底理解上面这些代码，以及记住代码中关于字符串相关函数的应用

/**
 * 逻辑运算
 * 算术运算
 * 赋值运算
 */

const str = '于艺和他的好朋友们';
// 截取字符串
str.substring(0, 3); // 123
// 从左往右搜索字符串索引位置
str.indexOf('好朋友');
// 从右往左搜索字符串索引位置
str.lastIndexOf('于艺')

//※ 判断字符串开头是不是某个字符串
str.startsWith('1');

// 判断字符串结尾是不是某个字符串
str.endsWith('6');

// 判断字符串中是否包含某个字符串 2中方式
str.includes('34656');
//※ 通过正则匹配字符串中符合条件的字符
str.match(/123/g); // 符合正则规则条件的字符串数组，如果没有匹配到任何值，则返回null

//※ 把字符串中所有的英文字母都改成小写
str.toLowerCase(); // lower 小写的

//※ 把字符串中所有的英文字母都改成大写
str.toUpperCase(); // upper 大写的

//※ 通过索引来查找字符串中对应的字符 2种方式
str.charAt(5);
str[11]; // 9

//※ 字符串拼接 2种方式
str + '11' + '24' + '3';
str.concat('11', '24', '3');


const str2 = '1,2,3,4,5,6,5,8,5,5,5';
// 以,分割字符串
str2.split(',')

//※ 返回字符串中某个索引位置的编码（utf-16）
str.charCodeAt(1); // char字符 code编码 at所在位置
str.codePointAt(1); // code编码 point点 at所在位置

// 类的静态函数
// string 类型 String 类
//※ 通过编码反查字符
String.fromCharCode(1);
String.fromCodePoint(1);

//※ 将字符串中的5替换10
str2.replace('5', '10')
//※ 将字符串中的5全部替换10 2种方式
str2.replaceAll('5','10');
//※ 通过正则的方式，替换字符串中符合条件的所有字符
str2.replace(/5/g, '10');


// 编程语言的语法结构：知道语言的结构，知道通过什么结构，可以表达什么含义
// API：语言当中的字词、短语。通过简短的函数或者变量，快速的实现我们想要的效果。

//
// const obj = {name: 'bob'};
// obj.name;
// obj['name']; // name 属性名
// let {name} = obj;
//
// const arr = [1, 2, 3];
// console.log(arr[2]);

let zip = '这是好几个字符ABCDefgh'
zip.startsWith('这是好');
zip.startsWith('这')
zip.startsWith('这是')

zip.endsWith('Defgh')

zip.match(/好几个/g);
zip.match(/好几个/g);
zip.match(/好几个/g);
console.log(zip.match(/好几个/g));

zip.toLowerCase();
zip.toLowerCase();
zip.toLowerCase();
zip.toLowerCase();
zip.toLowerCase();
zip.toLowerCase();

zip.toUpperCase();
zip.toUpperCase();
zip.toUpperCase();
zip.toUpperCase();
zip.toUpperCase();
zip.toUpperCase();

zip.charAt(2);
zip[2];
zip.charAt(6);
zip[6];
zip.charAt(3);
zip[3];
zip.charAt(5);
zip[5];
zip.charAt(1);
zip[1];
zip.charAt(4);
zip[4];

zip + '10'
zip.concat('20', '30', '5');
zip + '8'
zip.concat('8', '8', '8');
zip + '1'
zip.concat('1', '1', '1');

String.fromCharCode()
String.fromCodePoint()
String.fromCharCode()
String.fromCodePoint()
String.fromCharCode()
String.fromCodePoint()
String.fromCharCode()
String.fromCodePoint()

zip.replace('', '')
zip.replace('', '')
zip.replace('', '')
zip.replace('', '')
zip.replace('', '')

zip.replaceAll('','');
zip.replaceAll('','');
zip.replaceAll('','');
zip.replaceAll('','');
zip.replaceAll('','');
zip.replaceAll('','');

zip.replace(/1/g, '');
zip.replace(/1/g, '');
zip.replace(/1/g, '');
zip.replace(/1/g, '');
zip.replace(/1/g, '');
zip.replace(/1/g, '');
zip.replace(/1/g, '');